summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCharles Lombardo <clombardo169@gmail.com>2023-08-30 22:24:46 +0200
committerGitHub <noreply@github.com>2023-08-30 22:24:46 +0200
commita2f0caefd46df4a550adfb35c1f319becca1ffdb (patch)
tree12776fe4b53271faeda62f534d313f2a1fbfd929
parentMerge pull request #11419 from FearlessTobi/hwopus-2 (diff)
parentandroid: Separate emulation states from emulation mutex (diff)
downloadyuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar.gz
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar.bz2
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar.lz
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar.xz
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.tar.zst
yuzu-a2f0caefd46df4a550adfb35c1f319becca1ffdb.zip
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt103
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt110
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt59
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt77
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp14
-rw-r--r--src/android/app/src/main/jni/id_cache.h2
-rw-r--r--src/android/app/src/main/jni/native.cpp22
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml83
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml1
-rw-r--r--src/android/app/src/main/res/values/strings.xml2
28 files changed, 393 insertions, 237 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 5a7cf4ed7..c8706d7a6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
-import org.yuzu.yuzu_emu.utils.Log.error
-import org.yuzu.yuzu_emu.utils.Log.verbose
-import org.yuzu.yuzu_emu.utils.Log.warning
+import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
/**
@@ -465,7 +463,7 @@ object NativeLibrary {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
- warning("[NativeLibrary] EmulationActivity is null, can't exit.")
+ Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.")
return
}
@@ -490,15 +488,27 @@ object NativeLibrary {
}
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
- verbose("[NativeLibrary] Registering EmulationActivity.")
+ Log.verbose("[NativeLibrary] Registering EmulationActivity.")
sEmulationActivity = WeakReference(emulationActivity)
}
fun clearEmulationActivity() {
- verbose("[NativeLibrary] Unregistering EmulationActivity.")
+ Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
sEmulationActivity.clear()
}
+ @Keep
+ @JvmStatic
+ fun onEmulationStarted() {
+ sEmulationActivity.get()!!.onEmulationStarted()
+ }
+
+ @Keep
+ @JvmStatic
+ fun onEmulationStopped(status: Int) {
+ sEmulationActivity.get()!!.onEmulationStopped(status)
+ }
+
/**
* Logs the Yuzu version, Android version and, CPU.
*/
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index dbd602a1d..bbd328c71 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -28,6 +28,7 @@ import android.view.Surface
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@@ -41,6 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -70,8 +72,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val actionMute = "ACTION_EMULATOR_MUTE"
private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
+ private val emulationViewModel: EmulationViewModel by viewModels()
+
override fun onDestroy() {
stopForegroundService(this)
+ emulationViewModel.clear()
super.onDestroy()
}
@@ -416,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
}
+ fun onEmulationStarted() {
+ emulationViewModel.setEmulationStarted(true)
+ }
+
+ fun onEmulationStopped(status: Int) {
+ if (status == 0) {
+ finish()
+ }
+ }
+
private fun startMotionSensorListener() {
val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index e91277d35..13359ef36 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -3,8 +3,6 @@
package org.yuzu.yuzu_emu.adapters
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
@@ -15,23 +13,20 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
-import coil.load
-import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
-import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.utils.GameIconUtils
class GameAdapter(private val activity: AppCompatActivity) :
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
@@ -98,12 +93,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
this.game = game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
- activity.lifecycleScope.launch {
- val bitmap = decodeGameIcon(game.path)
- binding.imageGameScreen.load(bitmap) {
- error(R.drawable.default_icon)
- }
- }
+ GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
@@ -126,14 +116,4 @@ class GameAdapter(private val activity: AppCompatActivity) :
return oldItem == newItem
}
}
-
- private fun decodeGameIcon(uri: String): Bitmap? {
- val data = NativeLibrary.getIcon(uri)
- return BitmapFactory.decodeByteArray(
- data,
- 0,
- data.size,
- BitmapFactory.Options()
- )
- }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index a18efef19..6f4b5b13f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -4,43 +4,43 @@
package org.yuzu.yuzu_emu.disk_shader_cache
import androidx.annotation.Keep
+import androidx.lifecycle.ViewModelProvider
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment
+import org.yuzu.yuzu_emu.activities.EmulationActivity
+import org.yuzu.yuzu_emu.model.EmulationViewModel
+import org.yuzu.yuzu_emu.utils.Log
@Keep
object DiskShaderCacheProgress {
- val finishLock = Object()
- private lateinit var fragment: ShaderProgressDialogFragment
+ private lateinit var emulationViewModel: EmulationViewModel
- private fun prepareDialog() {
- val emulationActivity = NativeLibrary.sEmulationActivity.get()!!
- emulationActivity.runOnUiThread {
- fragment = ShaderProgressDialogFragment.newInstance(
- emulationActivity.getString(R.string.loading),
- emulationActivity.getString(R.string.preparing_shaders)
- )
- fragment.show(
- emulationActivity.supportFragmentManager,
- ShaderProgressDialogFragment.TAG
- )
- }
- synchronized(finishLock) { finishLock.wait() }
+ private fun prepareViewModel() {
+ emulationViewModel =
+ ViewModelProvider(
+ NativeLibrary.sEmulationActivity.get() as EmulationActivity
+ )[EmulationViewModel::class.java]
}
@JvmStatic
fun loadProgress(stage: Int, progress: Int, max: Int) {
val emulationActivity = NativeLibrary.sEmulationActivity.get()
- ?: error("[DiskShaderCacheProgress] EmulationActivity not present")
-
- when (LoadCallbackStage.values()[stage]) {
- LoadCallbackStage.Prepare -> prepareDialog()
- LoadCallbackStage.Build -> fragment.onUpdateProgress(
- emulationActivity.getString(R.string.building_shaders),
- progress,
- max
- )
- LoadCallbackStage.Complete -> fragment.dismiss()
+ if (emulationActivity == null) {
+ Log.error("[DiskShaderCacheProgress] EmulationActivity not present")
+ return
+ }
+
+ emulationActivity.runOnUiThread {
+ when (LoadCallbackStage.values()[stage]) {
+ LoadCallbackStage.Prepare -> prepareViewModel()
+ LoadCallbackStage.Build -> emulationViewModel.updateProgress(
+ emulationActivity.getString(R.string.building_shaders),
+ progress,
+ max
+ )
+
+ LoadCallbackStage.Complete -> {}
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt
deleted file mode 100644
index bf6f0366d..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.disk_shader_cache
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class ShaderProgressViewModel : ViewModel() {
- private val _progress = MutableLiveData(0)
- val progress: LiveData<Int> get() = _progress
-
- private val _max = MutableLiveData(0)
- val max: LiveData<Int> get() = _max
-
- private val _message = MutableLiveData("")
- val message: LiveData<String> get() = _message
-
- fun setProgress(progress: Int) {
- _progress.postValue(progress)
- }
-
- fun setMax(max: Int) {
- _max.postValue(max)
- }
-
- fun setMessage(msg: String) {
- _message.postValue(msg)
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
deleted file mode 100644
index 8a8e0a6e8..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.disk_shader_cache.ui
-
-import android.app.Dialog
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.DialogFragment
-import androidx.lifecycle.ViewModelProvider
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
-import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress
-import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel
-
-class ShaderProgressDialogFragment : DialogFragment() {
- private var _binding: DialogProgressBarBinding? = null
- private val binding get() = _binding!!
-
- private lateinit var alertDialog: AlertDialog
-
- private lateinit var shaderProgressViewModel: ShaderProgressViewModel
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- _binding = DialogProgressBarBinding.inflate(layoutInflater)
- shaderProgressViewModel =
- ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java]
-
- val title = requireArguments().getString(TITLE)
- val message = requireArguments().getString(MESSAGE)
-
- isCancelable = false
- alertDialog = MaterialAlertDialogBuilder(requireActivity())
- .setView(binding.root)
- .setTitle(title)
- .setMessage(message)
- .create()
- return alertDialog
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress ->
- binding.progressBar.progress = progress
- setUpdateText()
- }
- shaderProgressViewModel.max.observe(viewLifecycleOwner) { max ->
- binding.progressBar.max = max
- setUpdateText()
- }
- shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
- alertDialog.setMessage(msg)
- }
- synchronized(DiskShaderCacheProgress.finishLock) {
- DiskShaderCacheProgress.finishLock.notifyAll()
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- fun onUpdateProgress(msg: String, progress: Int, max: Int) {
- shaderProgressViewModel.setProgress(progress)
- shaderProgressViewModel.setMax(max)
- shaderProgressViewModel.setMessage(msg)
- }
-
- private fun setUpdateText() {
- binding.progressText.text = String.format(
- "%d/%d",
- shaderProgressViewModel.progress.value,
- shaderProgressViewModel.max.value
- )
- }
-
- companion object {
- const val TAG = "ProgressDialogFragment"
- const val TITLE = "title"
- const val MESSAGE = "message"
-
- fun newInstance(title: String, message: String): ShaderProgressDialogFragment {
- val frag = ShaderProgressDialogFragment()
- val args = Bundle()
- args.putString(TITLE, title)
- args.putString(MESSAGE, message)
- frag.arguments = args
- return frag
- }
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 53f19c4f8..944ae652e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -24,8 +24,9 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.isVisible
+import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Game
+import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.utils.*
@@ -66,6 +68,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game
+ private val emulationViewModel: EmulationViewModel by activityViewModels()
+
private var isInFoldableLayout = false
override fun onAttach(context: Context) {
@@ -130,9 +134,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
- // Setup overlay.
- updateShowFpsOverlay()
-
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
game.title
binding.inGameMenu.setNavigationItemSelectedListener {
@@ -174,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_exit -> {
emulationState.stop()
- requireActivity().finish()
+ emulationViewModel.setIsEmulationStopping(true)
+ binding.drawerLayout.close()
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
true
}
@@ -188,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireActivity(),
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
+ if (!NativeLibrary.isRunning()) {
+ return
+ }
+
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
} else {
@@ -204,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
.collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
}
}
+
+ GameIconUtils.loadGameIcon(game, binding.loadingImage)
+ binding.loadingTitle.text = game.title
+ binding.loadingTitle.isSelected = true
+ binding.loadingText.isSelected = true
+
+ emulationViewModel.shaderProgress.observe(viewLifecycleOwner) {
+ if (it > 0 && it != emulationViewModel.totalShaders.value!!) {
+ binding.loadingProgressIndicator.isIndeterminate = false
+
+ if (it < binding.loadingProgressIndicator.max) {
+ binding.loadingProgressIndicator.progress = it
+ }
+ }
+
+ if (it == emulationViewModel.totalShaders.value!!) {
+ binding.loadingText.setText(R.string.loading)
+ binding.loadingProgressIndicator.isIndeterminate = true
+ }
+ }
+ emulationViewModel.totalShaders.observe(viewLifecycleOwner) {
+ binding.loadingProgressIndicator.max = it
+ }
+ emulationViewModel.shaderMessage.observe(viewLifecycleOwner) {
+ if (it.isNotEmpty()) {
+ binding.loadingText.text = it
+ }
+ }
+
+ emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started ->
+ if (started) {
+ binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
+ ViewUtils.showView(binding.surfaceInputOverlay)
+ ViewUtils.hideView(binding.loadingIndicator)
+
+ // Setup overlay
+ updateShowFpsOverlay()
+ }
+ }
+
+ emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) {
+ if (it) {
+ binding.loadingText.setText(R.string.shutting_down)
+ ViewUtils.showView(binding.loadingIndicator)
+ ViewUtils.hideView(binding.inputContainer)
+ ViewUtils.hideView(binding.showFpsText)
+ }
+ }
}
override fun onConfigurationChanged(newConfig: Configuration) {
@@ -213,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.close()
}
if (EmulationMenuSettings.showOverlay) {
- binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false }
+ binding.surfaceInputOverlay.post {
+ binding.surfaceInputOverlay.visibility = View.VISIBLE
+ }
}
} else {
- if (EmulationMenuSettings.showOverlay) {
- binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true }
+ if (EmulationMenuSettings.showOverlay &&
+ emulationViewModel.emulationStarted.value == true
+ ) {
+ binding.surfaceInputOverlay.post {
+ binding.surfaceInputOverlay.visibility = View.VISIBLE
+ }
+ } else {
+ binding.surfaceInputOverlay.post {
+ binding.surfaceInputOverlay.visibility = View.INVISIBLE
+ }
}
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -226,9 +292,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
}
}
- if (!binding.surfaceInputOverlay.isInEditMode) {
- refreshInputOverlay()
- }
}
}
@@ -260,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onDetach()
}
- private fun refreshInputOverlay() {
- binding.surfaceInputOverlay.refreshControls()
- }
-
private fun resetInputOverlay() {
preferences.edit()
.remove(Settings.PREF_CONTROL_SCALE)
@@ -281,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val FRAMETIME = 2
val SPEED = 3
perfStatsUpdater = {
- val perfStats = NativeLibrary.getPerfStats()
- if (perfStats[FPS] > 0 && _binding != null) {
- binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
- }
-
- if (!emulationState.isStopped) {
+ if (emulationViewModel.emulationStarted.value == true) {
+ val perfStats = NativeLibrary.getPerfStats()
+ if (perfStats[FPS] > 0 && _binding != null) {
+ binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
+ }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
}
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
- binding.showFpsText.text = resources.getString(R.string.emulation_game_loading)
binding.showFpsText.visibility = View.VISIBLE
} else {
if (perfStatsUpdater != null) {
@@ -349,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
isInFoldableLayout = true
binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
- refreshInputOverlay()
}
}
it.isSeparating
@@ -437,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
.apply()
}
.setPositiveButton(android.R.string.ok) { _, _ ->
- refreshInputOverlay()
+ binding.surfaceInputOverlay.refreshControls()
}
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.emulation_toggle_all) { _, _ -> }
@@ -461,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_show_overlay -> {
it.isChecked = !it.isChecked
EmulationMenuSettings.showOverlay = it.isChecked
- refreshInputOverlay()
+ binding.surfaceInputOverlay.refreshControls()
true
}
@@ -567,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
preferences.edit()
.putInt(Settings.PREF_CONTROL_SCALE, scale)
.apply()
- refreshInputOverlay()
+ binding.surfaceInputOverlay.refreshControls()
}
private fun setControlOpacity(opacity: Int) {
preferences.edit()
.putInt(Settings.PREF_CONTROL_OPACITY, opacity)
.apply()
- refreshInputOverlay()
+ binding.surfaceInputOverlay.refreshControls()
}
private fun setInsets() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
new file mode 100644
index 000000000..e35f51bc3
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package org.yuzu.yuzu_emu.model
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class EmulationViewModel : ViewModel() {
+ private val _emulationStarted = MutableLiveData(false)
+ val emulationStarted: LiveData<Boolean> get() = _emulationStarted
+
+ private val _isEmulationStopping = MutableLiveData(false)
+ val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping
+
+ private val _shaderProgress = MutableLiveData(0)
+ val shaderProgress: LiveData<Int> get() = _shaderProgress
+
+ private val _totalShaders = MutableLiveData(0)
+ val totalShaders: LiveData<Int> get() = _totalShaders
+
+ private val _shaderMessage = MutableLiveData("")
+ val shaderMessage: LiveData<String> get() = _shaderMessage
+
+ fun setEmulationStarted(started: Boolean) {
+ _emulationStarted.postValue(started)
+ }
+
+ fun setIsEmulationStopping(value: Boolean) {
+ _isEmulationStopping.value = value
+ }
+
+ fun setShaderProgress(progress: Int) {
+ _shaderProgress.value = progress
+ }
+
+ fun setTotalShaders(max: Int) {
+ _totalShaders.value = max
+ }
+
+ fun setShaderMessage(msg: String) {
+ _shaderMessage.value = msg
+ }
+
+ fun updateProgress(msg: String, progress: Int, max: Int) {
+ setShaderMessage(msg)
+ setShaderProgress(progress)
+ setTotalShaders(max)
+ }
+
+ fun clear() {
+ _emulationStarted.value = false
+ _isEmulationStopping.value = false
+ _shaderProgress.value = 0
+ _totalShaders.value = 0
+ _shaderMessage.value = ""
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
new file mode 100644
index 000000000..c0fe596d7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -0,0 +1,77 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.widget.ImageView
+import androidx.core.graphics.drawable.toDrawable
+import coil.ImageLoader
+import coil.decode.DataSource
+import coil.fetch.DrawableResult
+import coil.fetch.FetchResult
+import coil.fetch.Fetcher
+import coil.key.Keyer
+import coil.memory.MemoryCache
+import coil.request.ImageRequest
+import coil.request.Options
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.Game
+
+class GameIconFetcher(
+ private val game: Game,
+ private val options: Options
+) : Fetcher {
+ override suspend fun fetch(): FetchResult {
+ return DrawableResult(
+ drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
+ isSampled = false,
+ dataSource = DataSource.DISK
+ )
+ }
+
+ private fun decodeGameIcon(uri: String): Bitmap? {
+ val data = NativeLibrary.getIcon(uri)
+ return BitmapFactory.decodeByteArray(
+ data,
+ 0,
+ data.size,
+ BitmapFactory.Options()
+ )
+ }
+
+ class Factory : Fetcher.Factory<Game> {
+ override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher =
+ GameIconFetcher(data, options)
+ }
+}
+
+class GameIconKeyer : Keyer<Game> {
+ override fun key(data: Game, options: Options): String = data.path
+}
+
+object GameIconUtils {
+ private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext)
+ .components {
+ add(GameIconKeyer())
+ add(GameIconFetcher.Factory())
+ }
+ .memoryCache {
+ MemoryCache.Builder(YuzuApplication.appContext)
+ .maxSizePercent(0.25)
+ .build()
+ }
+ .build()
+
+ fun loadGameIcon(game: Game, imageView: ImageView) {
+ val request = ImageRequest.Builder(YuzuApplication.appContext)
+ .data(game)
+ .target(imageView)
+ .error(R.drawable.default_icon)
+ .build()
+ imageLoader.enqueue(request)
+ }
+}
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 9cbbf23a3..960abf95a 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class;
static jclass s_load_callback_stage_class;
static jmethodID s_exit_emulation_activity;
static jmethodID s_disk_cache_load_progress;
+static jmethodID s_on_emulation_started;
+static jmethodID s_on_emulation_stopped;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
@@ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() {
return s_disk_cache_load_progress;
}
+jmethodID GetOnEmulationStarted() {
+ return s_on_emulation_started;
+}
+
+jmethodID GetOnEmulationStopped() {
+ return s_on_emulation_stopped;
+}
+
} // namespace IDCache
#ifdef __cplusplus
@@ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
+ s_on_emulation_started =
+ env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
+ s_on_emulation_stopped =
+ env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index be535fe1e..b76158928 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass();
jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress();
+jmethodID GetOnEmulationStarted();
+jmethodID GetOnEmulationStopped();
} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index b2adfdeda..0f2a6d9e4 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -203,12 +203,10 @@ public:
}
bool IsRunning() const {
- std::scoped_lock lock(m_mutex);
return m_is_running;
}
bool IsPaused() const {
- std::scoped_lock lock(m_mutex);
return m_is_running && m_is_paused;
}
@@ -335,6 +333,8 @@ public:
// Tear down the render window.
m_window.reset();
+
+ OnEmulationStopped(m_load_result);
}
void PauseEmulation() {
@@ -376,6 +376,8 @@ public:
m_system.InitializeDebugger();
}
+ OnEmulationStarted();
+
while (true) {
{
[[maybe_unused]] std::unique_lock lock(m_mutex);
@@ -511,6 +513,18 @@ private:
static_cast<jint>(progress), static_cast<jint>(max));
}
+ static void OnEmulationStarted() {
+ JNIEnv* env = IDCache::GetEnvForThread();
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
+ IDCache::GetOnEmulationStarted());
+ }
+
+ static void OnEmulationStopped(Core::SystemResultStatus result) {
+ JNIEnv* env = IDCache::GetEnvForThread();
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
+ IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
+ }
+
private:
static EmulationSession s_instance;
@@ -528,8 +542,8 @@ private:
Core::PerfStatsResults m_perf_stats{};
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
- bool m_is_running{};
- bool m_is_paused{};
+ std::atomic<bool> m_is_running = false;
+ std::atomic<bool> m_is_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index e54a10e8f..da97d85c1 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -26,6 +26,81 @@
android:focusable="false"
android:focusableInTouchMode="false" />
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/loading_indicator"
+ style="?attr/materialCardViewOutlinedStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:focusable="false">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/loading_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal">
+
+ <ImageView
+ android:id="@+id/loading_image"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:adjustViewBounds="true"
+ app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/linearLayout"
+ tools:src="@drawable/default_icon" />
+
+ <LinearLayout
+ android:id="@+id/linearLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="36dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/loading_image"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/loading_title"
+ style="@style/TextAppearance.Material3.TitleMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:requiresFadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ tools:text="@string/games" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/loading_text"
+ style="@style/TextAppearance.Material3.TitleSmall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:requiresFadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/loading"
+ android:textAlignment="viewStart" />
+
+ <com.google.android.material.progressindicator.LinearProgressIndicator
+ android:id="@+id/loading_progress_indicator"
+ android:layout_width="192dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:indeterminate="true"
+ app:trackCornerRadius="8dp" />
+
+ </LinearLayout>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ </com.google.android.material.card.MaterialCardView>
+
</FrameLayout>
<FrameLayout
@@ -41,11 +116,12 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="true"
- android:focusableInTouchMode="true" />
+ android:focusableInTouchMode="true"
+ android:visibility="invisible" />
<Button
- style="@style/Widget.Material3.Button.ElevatedButton"
android:id="@+id/done_control_config"
+ style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -81,6 +157,7 @@
android:layout_height="match_parent"
android:layout_gravity="start|bottom"
app:headerLayout="@layout/header_in_game"
- app:menu="@menu/menu_in_game" />
+ app:menu="@menu/menu_in_game"
+ tools:visibility="gone" />
</androidx.drawerlayout.widget.DrawerLayout>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 0c1d91264..daaa7ffde 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -209,7 +209,6 @@
<string name="emulation_pause">Emulation pausieren</string>
<string name="emulation_unpause">Emulation fortsetzen</string>
<string name="emulation_input_overlay">Overlay-Optionen</string>
- <string name="emulation_game_loading">Spiel lädt…</string>
<string name="load_settings">Lädt Einstellungen...</string>
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 357f956d1..e9129cb00 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Pausar Emulación</string>
<string name="emulation_unpause">Reanudar Emulación</string>
<string name="emulation_input_overlay">Opciones de pantalla </string>
- <string name="emulation_game_loading">Cargando juego...</string>
<string name="load_settings">Cargando configuración...</string>
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index dfca1c830..2d99d618e 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Mettre en pause l\'émulation</string>
<string name="emulation_unpause">Reprendre l\'émulation</string>
<string name="emulation_input_overlay">Options de l\'overlay</string>
- <string name="emulation_game_loading">Chargement du jeu...</string>
<string name="load_settings">Chargement des paramètres…</string>
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 089d93ed6..d9c3de385 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Metti in pausa l\'emulazione</string>
<string name="emulation_unpause">Riprendi Emulazione</string>
<string name="emulation_input_overlay">Impostazioni Overlay</string>
- <string name="emulation_game_loading">Caricamento del gioco...</string>
<string name="load_settings">Caricamento delle impostazioni...</string>
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index 39b590bee..7a226cd5c 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -211,7 +211,6 @@
<string name="emulation_pause">エミュレーションを一時停止</string>
<string name="emulation_unpause">エミュレーションを再開</string>
<string name="emulation_input_overlay">オーバーレイオプション</string>
- <string name="emulation_game_loading">ロード中…</string>
<string name="load_settings">設定をロード中…</string>
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index cbcb2873f..427b6e5a0 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">에뮬레이션 일시 중지</string>
<string name="emulation_unpause">에뮬레이션 일시 중지 해제</string>
<string name="emulation_input_overlay">오버레이 옵션</string>
- <string name="emulation_game_loading">게임 불러오기 중...</string>
<string name="load_settings">설정 불러오기 중...</string>
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index e48a4be38..ce8d7a9e4 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Pause Emulering</string>
<string name="emulation_unpause">Opphev pausing av emulering</string>
<string name="emulation_input_overlay">Alternativer for overlegg</string>
- <string name="emulation_game_loading">Spillet lastes inn...</string>
<string name="load_settings">Laster inn innstillinger...</string>
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index bc9c0f7f4..c2c24b48f 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Wstrzymaj emulację</string>
<string name="emulation_unpause">Wznów emulację</string>
<string name="emulation_input_overlay">Opcje nakładki</string>
- <string name="emulation_game_loading">Wczytywanie gry...</string>
<string name="load_settings">Wczytywanie ustawień...</string>
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index 75fe0edbf..04f276108 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Pausa emulação</string>
<string name="emulation_unpause">Retomar emulação</string>
<string name="emulation_input_overlay">Opções de sobreposição </string>
- <string name="emulation_game_loading">Jogo a carregar...</string>
<string name="load_settings">Configurações a carregar...</string>
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 96b040c66..66a3a1a2e 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Pausa emulação</string>
<string name="emulation_unpause">Retomar emulação</string>
<string name="emulation_input_overlay">Opções de sobreposição </string>
- <string name="emulation_game_loading">Jogo a carregar...</string>
<string name="load_settings">Configurações a carregar...</string>
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 8d954f59e..f770e954f 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Пауза эмуляции</string>
<string name="emulation_unpause">Возобновление эмуляции</string>
<string name="emulation_input_overlay">Настройки оверлея</string>
- <string name="emulation_game_loading">Загрузка игры...</string>
<string name="load_settings">Загрузка настроек...</string>
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 6c028535b..ea3ab1b15 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">Пауза емуляції</string>
<string name="emulation_unpause">Відновлення емуляції</string>
<string name="emulation_input_overlay">Налаштування оверлея</string>
- <string name="emulation_game_loading">Завантаження гри...</string>
<string name="load_settings">Завантаження налаштувань...</string>
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index e4ad2ed07..b45a5a528 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">暂停模拟</string>
<string name="emulation_unpause">继续模拟</string>
<string name="emulation_input_overlay">虚拟按键选项</string>
- <string name="emulation_game_loading">载入游戏中…</string>
<string name="load_settings">正在载入设定…</string>
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index 0d32f23df..3aab889e4 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -213,7 +213,6 @@
<string name="emulation_pause">暫停模擬</string>
<string name="emulation_unpause">取消暫停模擬</string>
<string name="emulation_input_overlay">覆疊選項</string>
- <string name="emulation_game_loading">遊戲正在載入…</string>
<string name="load_settings">正在載入設定…</string>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index d43891cec..b163e6fc1 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -204,6 +204,7 @@
<string name="error_saving">Error saving %1$s.ini: %2$s</string>
<string name="unimplemented_menu">Unimplemented Menu</string>
<string name="loading">Loading…</string>
+ <string name="shutting_down">Shutting down…</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="reset_to_default">Reset to default</string>
<string name="reset_all_settings">Reset all settings?</string>
@@ -262,7 +263,6 @@
<string name="emulation_pause">Pause emulation</string>
<string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string>
- <string name="emulation_game_loading">Game loading…</string>
<string name="load_settings">Loading settings…</string>